package utils

import (
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/lib/pq"
	"gorm.io/gorm"
)

func GetQueryDB(o any, db *gorm.DB, c *gin.Context, excludeFields ...string) *gorm.DB {
	val := reflect.ValueOf(o).Elem()
	typ := reflect.TypeOf(o).Elem()

	now := time.Now()
	var timeType = reflect.TypeOf(now)
	var timePtType = reflect.TypeOf(&now)

	for i := 0; i < val.NumField(); i = i + 1 {
		// fv := val.Field(i)
		ft := typ.Field(i)

		// gorm是否忽略
		if ft.Tag.Get("gorm") == "-" {
			continue
		}

		// json名称
		jsonName := ft.Name
		jsonTag := ft.Tag.Get("json")
		if jsonTag != "" {
			if strings.Contains(jsonTag, ",") {
				// 处理类似这样的情况：`json:"password,omitempty"`
				jsonName = strings.Split(jsonTag, ",")[0]
			} else {
				jsonName = jsonTag
			}
		}

		if excludeFields != nil && len(excludeFields) > 0 && IsInArray(jsonName, excludeFields) {
			continue
		}

		// 判断是否是嵌套结构
		if ft.Type.Kind() == reflect.Struct && ft.Type != timeType && ft.Type != timePtType {
			structField := ft.Type
			for j := 0; j < structField.NumField(); j++ {
				jsonName = structField.Field(j).Name
				tag := structField.Field(j).Tag.Get("json")
				if tag != "" {
					a := strings.Split(tag, ",")
					if len(a) > 0 {
						jsonName = a[0]
					}
				}
				db = setQueryDB(c, db, structField.Field(j).Type, jsonName)
			}
		} else {
			db = setQueryDB(c, db, ft.Type, jsonName)
		}
	}

	return db
}

func setQueryDB(c *gin.Context, db *gorm.DB, fieldType reflect.Type, jsonName string) *gorm.DB {
	str := ""
	var stringType = reflect.TypeOf(str)
	var stringPtType = reflect.TypeOf(&str)

	var intType = reflect.TypeOf(int(1))
	var int64Type = reflect.TypeOf(int64(1))
	var float32Type = reflect.TypeOf(float32(1))
	var float64Type = reflect.TypeOf(float64(1))
	var boolType = reflect.TypeOf(true)

	var stringArrayType = reflect.TypeOf(pq.StringArray{})
	var int64ArrayType = reflect.TypeOf(pq.Int64Array{})

	now := time.Now()
	var timeType = reflect.TypeOf(now)
	var timePtType = reflect.TypeOf(&now)

	var dbFieldName = "\"" + ToSnakeCase(jsonName) + "\""
	switch fieldType {
	case stringType, stringPtType:
		if !IsEmptyValueStr(c.Query(jsonName)) {
			if jsonName == "id" {
				db = db.Where(jsonName+" = ?", c.Query(jsonName))
			} else {
				db = db.Where(dbFieldName+" ilike '%'||?||'%'", c.Query(jsonName))
			}
		}
		if !IsEmptyValueStr(c.Query(jsonName + "_min")) {
			db = db.Where(dbFieldName+" >= ?", c.Query(jsonName+"_min"))
		}
		if !IsEmptyValueStr(c.Query(jsonName + "_max")) {
			db = db.Where(dbFieldName+" <= ?", c.Query(jsonName+"_max"))
		}
		break
	case intType, int64Type, float32Type, float64Type, boolType, timeType, timePtType:
		if !IsEmptyValueStr(c.Query(jsonName)) {
			db = db.Where(dbFieldName+" = ?", c.Query(jsonName))
		}

		var minValue = c.Query(jsonName + "_min")
		if !IsEmptyValueStr(minValue) {
			if (fieldType == timeType || fieldType == timePtType) && IsNumber(minValue) {
				value, _ := strconv.ParseInt(minValue, 10, 64)
				var minTime = time.Unix(value/1000, 0)
				db = db.Where(dbFieldName+" >= ?", minTime)
			} else {
				db = db.Where(dbFieldName+" >= ?", minValue)
			}
		}

		var maxValue = c.Query(jsonName + "_max")
		if !IsEmptyValueStr(maxValue) {
			if (fieldType == timeType || fieldType == timePtType) && IsNumber(maxValue) {
				value, _ := strconv.ParseInt(maxValue, 10, 64)
				var maxTime = time.Unix(value/1000, 0)
				db = db.Where(dbFieldName+" <= ?", maxTime)
			} else {
				db = db.Where(dbFieldName+" <= ?", maxValue)
			}
		}
		break
	case stringArrayType, int64ArrayType:
		itemName := jsonName
		if strings.HasSuffix(jsonName, "List") {
			itemName = jsonName[0 : len(jsonName)-4]
		} else if strings.HasSuffix(jsonName, "Array") {
			itemName = jsonName[0 : len(jsonName)-5]
		} else if strings.HasSuffix(jsonName, "Arr") {
			itemName = jsonName[0 : len(jsonName)-3]
		} else if strings.HasSuffix(jsonName, "s") {
			itemName = jsonName[0 : len(jsonName)-1]
		}
		if fieldType == int64ArrayType {
			if !IsEmptyValueStr(c.Query(itemName)) {
				db = db.Where("? = any("+dbFieldName+")", c.Query(itemName))
			}
		} else {
			if !IsEmptyValueStr(c.Query(itemName)) {
				db = db.Where("? = any("+dbFieldName+")", c.Query(itemName))
			}
		}
		break
	}
	return db
}

func PageQuery[T any](db *gorm.DB, tableName string, data *[]T, pageInfo *PageInfo) PageList[T] {
	// 分页内容
	var total int64
	db.Table(tableName).Count(&total)
	pageInfo.ResetByTotal(total)

	// 分页查询
	db.Table(tableName).Limit(int(pageInfo.GetLimit())).Offset(int(pageInfo.GetOffset())).Find(data)

	return PageList[T]{
		PageInfo: *pageInfo,
		List:     data,
	}
}
